﻿
// ©Copyright 2012 by James Plotts.  All rights reserved under the Eclipse Public License v 1.0.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using EternalCodeworks.EighthDwarf.Engine;


namespace EighthDwarf
{
    public partial class Game1 : Microsoft.Xna.Framework.Game
    {
        



        public BoundingBox CalculateBoundingBox(Model model) 
        {
            // Initialize minimum and maximum corners of the bounding box to max and min values
            Vector3 min = new Vector3(Single.MaxValue);
            Vector3 max = new Vector3(Single.MinValue);

            //BoundingBox bb = BoundingBox.CreateFromPoints(model.Meshes);

            Matrix[] transforms = new Matrix[model.Bones.Count]; 
            model.CopyAbsoluteBoneTransformsTo(transforms);

            List<BoundingBox> bblist = new List<BoundingBox>();
            
            foreach (ModelMesh mesh in model.Meshes)
            {
                bblist.Add(BuildBoundingBox(mesh, transforms[mesh.ParentBone.Index]));
            }
            BoundingBox finalbb = bblist[0];
            if (bblist.Count > 0) 
            {
                for (Int32 i = 1; i < bblist.Count; i++)
                {
                    finalbb = BoundingBox.CreateMerged(finalbb, bblist[i]);
                }
            }

            // Create and return bounding box
            return finalbb;
        }

        
        public BoundingBox BuildBoundingBox(ModelMesh mesh, Matrix meshtransform) 
        {
            // Create initial variables to hold min and max xyz values for the mesh
            Vector3 meshMax = new Vector3(Single.MinValue);
            Vector3 meshMin = new Vector3(Single.MaxValue);
            
            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                // The stride is how big, in bytes, one vertex is in the vertex buffer
                // We have to use this as we do not know the make up of the vertex
                Int32 stride = part.VertexBuffer.VertexDeclaration.VertexStride;

                Int32 singlelen = sizeof(Single);
                Single[] vertexdata = new Single[part.NumVertices * stride]; 
                part.VertexBuffer.GetData(part.VertexOffset * stride, vertexdata, 0, part.NumVertices, stride);

                // Find minimum and maximum xyz values for this mesh part
                Vector3 vertposition; 
                for (Int32 i = 0; i <= vertexdata.Length / singlelen ; i = i + stride / singlelen)
                {
                    vertposition = new Vector3(vertexdata[i], vertexdata[i + 1], vertexdata[i + 2]);
                    // update our values from this vertex
                    meshMin = Vector3.Min(meshMin, vertposition);
                    meshMax = Vector3.Max(meshMax, vertposition);
                }
            }

            // transform by mesh bone matrix
            meshMin = Vector3.Transform(meshMin, meshtransform);
            meshMax = Vector3.Transform(meshMax, meshtransform);

            //// transform by world matrix
            //meshMin = Vector3.Transform(meshMin, worldtrans);
            //meshMax = Vector3.Transform(meshMax, worldtrans);

            // Return the bounding box
            return new BoundingBox(meshMin, meshMax);
        }


        
 
        public Ray CalculateRay(Vector2 mouseLocation, Matrix view, Matrix projection, Viewport viewport )
        {
            Vector3 nearPoint = viewport.Unproject(new Vector3(mouseLocation.X,
                    mouseLocation.Y, 0),
                    projection,
                    view,
                    Matrix.Identity);

            Vector3 farPoint = viewport.Unproject(new Vector3(mouseLocation.X,
                   mouseLocation.Y, 1),
                   projection,
                   view,
                   Matrix.Identity);

            //  here is the fix to the problem:
            //  transform the camera ray points with the inverse of the camera transforms
            nearPoint = Vector3.Transform(nearPoint, Matrix.Invert(CurrentCameraTransform));
            farPoint = Vector3.Transform(farPoint, Matrix.Invert(CurrentCameraTransform));

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();


            return new Ray(nearPoint, direction);

        }

        public Single? IntersectDistance(BoundingSphere sphere, Vector2 mouseLocation, Matrix view, Matrix projection, Viewport viewport ) 
        {
            Ray mouseRay = CalculateRay(mouseLocation, view, projection, viewport);
            return mouseRay.Intersects(sphere);
        }


        public Single? IntersectDistanceBox(BoundingBox box, Vector2 mouseLocation, Matrix view, Matrix projection, Viewport viewport ) 
        {
            Ray mouseRay = CalculateRay(mouseLocation, view, projection, viewport);
            return mouseRay.Intersects(box);
        }

        public Single? Intersects(Vector2 mouseLocation,
            Model model, Matrix view, Matrix projection,
            Viewport viewport, BoundingBox bb, Matrix worldtrans)
        {
            Single? distance = null;
            if (model != null)
                distance = IntersectDistanceBox(bb, mouseLocation, view, projection, viewport);

            return distance;
        }


    }
}
